package com.eascs.workflow.wf.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.eascs.app.log.Logger;
import com.eascs.app.log.LoggerFactory;
import com.eascs.common.util.StringUtil;
import com.eascs.rabbitmq.common.CommonConst;
import com.eascs.rabbitmq.common.Producer;
import com.eascs.rabbitmq.entity.Message;
import com.eascs.web.sso.User;
import com.eascs.web.sso.UserHolder;
import com.eascs.workflow.common.entity.sys.ActOrgEntity;
import com.eascs.workflow.common.entity.sys.HREmployeeAuditor;
import com.eascs.workflow.common.entity.sys.SysRole;
import com.eascs.workflow.common.entity.wf.DicData;
import com.eascs.workflow.common.entity.wf.NodeTransferResult;
import com.eascs.workflow.common.entity.wf.ProcessNodeTemplate;
import com.eascs.workflow.common.entity.wf.ProcessTaskMain;
import com.eascs.workflow.common.entity.wf.ProcessTaskNode;
import com.eascs.workflow.common.entity.wf.ProcessTemplate;
import com.eascs.workflow.common.entity.wf.TaskNodeAuditor;
import com.eascs.workflow.common.service.wf.WorkFlowNodeTransferCheck;
import com.eascs.workflow.common.service.wf.WorkFlowNotify;
import com.eascs.workflow.common.vo.CancleProcessParam;
import com.eascs.workflow.common.vo.CloseProcessParam;
import com.eascs.workflow.common.vo.ModelResult;
import com.eascs.workflow.common.vo.WFNodeCheckParam;
import com.eascs.workflow.common.vo.WFNotifyParam;
import com.eascs.workflow.common.vo.WFRequestParam;
import com.eascs.workflow.dictionary.service.DicDataServiceInlet;
import com.eascs.workflow.email.service.WFEmailService;
import com.eascs.workflow.email.service.WFSmsService;
import com.eascs.workflow.email.service.WechatService;
import com.eascs.workflow.email.service.impl.WFSmsServiceImpl;
import com.eascs.workflow.org.dao.OrganRoleUserDao;
import com.eascs.workflow.org.entity.OrgRoleUser;
import com.eascs.workflow.org.entity.Organ;
import com.eascs.workflow.org.service.OrganService;
import com.eascs.workflow.processAuthorization.dao.impl.ProcessAuthorizationDao;
import com.eascs.workflow.sys.dao.SysOrgDao;
import com.eascs.workflow.sys.dao.SysRoleDao;
import com.eascs.workflow.sys.dao.SysUserDao;
import com.eascs.workflow.timeEffective.utils.CalculateTimeUtils;
import com.eascs.workflow.wf.dao.ProcessNodeTemplateDao;
import com.eascs.workflow.wf.dao.ProcessTaskMainDao;
import com.eascs.workflow.wf.dao.ProcessTaskNodeDao;
import com.eascs.workflow.wf.dao.ProcessTemplateDao;
import com.eascs.workflow.wf.dao.TaskNodeAuditorDao;
import com.eascs.workflow.wf.service.WFProcessService;
import com.eascs.workflow.wf.utils.ProcessCodeBuilder;
import com.eascs.workflow.wf.utils.WFParamCheckUtils;
import com.eascs.workflow.wf.vo.TransferNodeParam;

@Service
public class WFProcessServiceImpl implements WFProcessService {
	private static Logger log = LoggerFactory.getLogger(WFProcessServiceImpl.class);
	@Autowired
	private ProcessTemplateDao processTemplateDao;
	@Autowired
	private ProcessNodeTemplateDao processNodeTemplateDao;
	@Autowired
	private ProcessTaskMainDao processTaskMainDao;
	@Autowired
	private ProcessTaskNodeDao processTaskNodeDao;
	@Autowired
	private TaskNodeAuditorDao taskNodeAuditorDao;
	@Autowired
	private SysUserDao sysUserDao;
	@Autowired
	private OrganService organService;
	@Autowired
	private SysOrgDao sysOrgDao;
	@Autowired
	private SysRoleDao sysRoleDao;
	@Autowired
	private OrganRoleUserDao organRoleUserDao;
	@Autowired
	private WFEmailService wFEmailService;
	@Reference
	private WorkFlowNodeTransferCheck workFlowNodeTransferCheck;
	@Reference
	private WorkFlowNotify workFlowNotify;
	@Autowired
	private WFSmsService wfSmsService;
	@Autowired
	private CalculateTimeUtils calculateTimeUtils;
	@Autowired
	private WechatService wechatService;
	@Autowired
	private DicDataServiceInlet dicDataServiceInlet;
	@Autowired
	private Producer producer;
	@Autowired
	private ProcessAuthorizationDao processAuthorizationDao;

	@Transactional("wfTransactionManager")
	public ModelResult<ProcessTaskMain> instance(WFRequestParam requestParam) {
		log.info("[{}],开始启动流程", requestParam);
		// 参数校验
		ModelResult<Boolean> checkResult = WFParamCheckUtils.checkWFRequestParam(requestParam);
		if (!checkResult.isSuccess()) {
			log.info(checkResult.getErrMsg());
			return new ModelResult<ProcessTaskMain>(checkResult.getErrCode(), checkResult.getErrMsg());
		}
		// 检查流程模板是否存在
		ProcessTemplate processTemplate = processTemplateDao.getPocessTemplate(requestParam.getSystemModule(),
				requestParam.getProcessType());
		if (processTemplate == null) {
			log.info("referId:[{}],processType:[{}],systemModule:[{}],启动流程,模板不存在", requestParam.getReferId(),
					requestParam.getProcessType(), requestParam.getSystemModule());
			return new ModelResult<ProcessTaskMain>("process.template.not.exist", "流程模板不存在！");
		}
		// 检查流程是否启动 referid + processId
		ProcessTaskMain queryTaskMain = processTaskMainDao.checkProcessStart(requestParam.getReferId(),
				processTemplate.getProcessNo());
		if (queryTaskMain != null
				&& !StringUtils.equals(requestParam.getProcessType(), ProcessTemplate.PROECESS_TYPE_BORROW_ARCHIVE)) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],流程已经启动，不能重复启动",
					requestParam.getReferId(), requestParam.getReferCode(), processTemplate.getProcessName(),
					processTemplate.getSystemModule());
			return new ModelResult<ProcessTaskMain>("process.task.alreay.instance", "流程已经启动");
		}
		// 检查流程节点模板是否存在
		List<String> excludeNodeCodeList = requestParam.getExcludeNodeCodeList();
		List<ProcessNodeTemplate> nodeTemplateList = processNodeTemplateDao
				.getByProcessId(processTemplate.getProcessNo(), excludeNodeCodeList);
		if (nodeTemplateList == null || nodeTemplateList.isEmpty()) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],启动流程,节点模板不存在",
					requestParam.getReferId(), requestParam.getReferCode(), processTemplate.getProcessName(),
					processTemplate.getSystemModule());
			return new ModelResult<ProcessTaskMain>("process.node.template.not.exist", "流程节点模板不存在！");
		}
		// 经办平台 unitNo
		String orgNo = requestParam.getOrgNo();
		// check 每个节点的审核人是否存在
		ModelResult<Map<String, List<User>>> modelResult = searchNodeAuditor(processTemplate, orgNo, nodeTemplateList,
				requestParam.getRoleUserMap(), requestParam.getAppliedby());
		if (!modelResult.isSuccess()) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],[{}]", requestParam.getReferId(),
					requestParam.getReferCode(), processTemplate.getProcessName(), processTemplate.getSystemModule(),
					modelResult.getErrMsg());
			return new ModelResult<ProcessTaskMain>(modelResult.getErrCode(), modelResult.getErrMsg());
		}

		// 检查是否是驳回后新启动流程
		ProcessTaskMain processTaskMain = processTaskMainDao.getNewlyTurnedDownTaskMain(requestParam.getReferId(),
				processTemplate.getProcessNo());
		// 流程节点的父节点
		ProcessTaskNode parentTaskNode = null;
		// 驳回节点的seq
		int nodeSeqStart = 0;
		if (processTaskMain != null) {// 有驳回的流程
			// 处理驳回的流程
			parentTaskNode = handTurnedDownForProcessInstance(processTaskMain);
			nodeSeqStart = parentTaskNode.getNodeSeq() + 1;
		} else {
			// 新建插入主流程
			processTaskMain = insertProcessTaskMain(requestParam, processTemplate, nodeTemplateList);
		}

		// 节点对应的审核人
		Map<String, List<User>> auditorsMap = modelResult.getModel();
		// 需要发送邮件的待办人
		List<TaskNodeAuditor> mailToList = new ArrayList<TaskNodeAuditor>();
		// 审核中节点名称
		ProcessTaskNode auditingNode = null;
		// 实例化流程节点
		for (int nodeSeq = 0; nodeSeq < nodeTemplateList.size(); nodeSeq++) {
			ProcessNodeTemplate nodeTemplate = nodeTemplateList.get(nodeSeq);
			// 构建流程节点
			ProcessTaskNode newTaskNode = buildTaskNode(processTaskMain, nodeTemplate, nodeSeq, parentTaskNode,
					nodeSeqStart);
			// 保存流程节点
			ProcessTaskNode saveTaskNode = processTaskNodeDao.insert(newTaskNode);
			if (saveTaskNode == null) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],nodeName:[{}],节点信息插入失败",
						requestParam.getReferId(), requestParam.getReferCode(), processTemplate.getProcessName(),
						processTemplate.getSystemModule(), newTaskNode.getNodeName());
				throw new RuntimeException("流程节点插入失败"); // 回滚事物
			}
			// 流程节点的父节点
			parentTaskNode = saveTaskNode;
			// 修改主流程的当前审核节点
			if (nodeSeq == 1) {
				processTaskMain.setCurNodeId(saveTaskNode.getTaskNodeId());// 当前审核节点
				processTaskMain.setCurNodeStime(new Date());// 审核节点开始时间
				int effectRow = processTaskMainDao.updateById(processTaskMain);
				if (effectRow <= 0) {
					throw new RuntimeException("启动流程失败,修改流程当前审核节点失败");
				}
				// 审核中的节点
				auditingNode = saveTaskNode;
			}

			// 排除开始节点和结束节点 ,结束节点没有审核人
			if (!nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {
				List<User> auditors = new ArrayList<User>();
				// 开始节点审核人为经办人
				if (nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)) {
					// 查找申请人信息
					User auditor = sysUserDao.getSysUserById(requestParam.getAppliedby());
					if (auditor == null) {
						return new ModelResult<ProcessTaskMain>("process.node.init.auditor.not.exist", "申请人不存在！");
					}
					auditors.add(auditor);
				} else {
					// 实例化节点审核人
					auditors = auditorsMap.get(nodeTemplate.getNodeId());
					// 同一个组织，同一个角色，有多个人，修改节点状态为 且
					if (auditors.size() > 1) {
						// 修改为 OR 节点
						int effectRow = processTaskNodeDao.updateNodeType(saveTaskNode.getTaskNodeId(),
								ProcessNodeTemplate.NODE_TYPE_OR);
						if (effectRow <= 0) {
							throw new RuntimeException("修改流程节点类型失败");
						}
					}
				}
				// 审核人列表
				for (User auditor : auditors) {
					TaskNodeAuditor newNodeAuditor = initNodeAuditor(saveTaskNode, auditor, nodeSeq);
					// 保存节点审核人
					TaskNodeAuditor saveNodeAuditor = taskNodeAuditorDao.insert(newNodeAuditor);
					if (saveNodeAuditor == null) {
						log.info(
								"referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],nodeName:[{}],节点审核人插入失败",
								processTaskMain.getReferId(), processTaskMain.getReferCode(),
								processTaskMain.getProcessName(), processTaskMain.getSystemModule(),
								saveTaskNode.getNodeName());
						throw new RuntimeException("节点审核人插入失败"); // 回滚事物
					}
					if (nodeSeq == 1) {// 审核中的人发送邮件
						// 发送邮件的列表
						mailToList.add(saveNodeAuditor);
					}
				}
			}
		}

		// 处理流程授权业务
		handProcessAuthorize(processTaskMain);
		// 开始通知业务系统
		notifyBizSystem(processTaskMain, auditingNode);
		// 发邮件,给待办人发邮件
		wFEmailService.notifyEmailToDo(processTaskMain, mailToList, auditingNode);
		// 微信消息通知
		wechatService.notifyWechatToDo(processTaskMain, mailToList, auditingNode);
		log.info("[{}],启动流程成功", requestParam);
		return new ModelResult<ProcessTaskMain>().withModel(processTaskMain);
	}

	/**
	 * 清理驳回的流程
	 * 
	 * @param processTaskMain
	 */
	private ProcessTaskNode handTurnedDownForProcessInstance(ProcessTaskMain processTaskMain) {
		// 清除多余的节点和审核人
		List<ProcessTaskNode> nodeList = processTaskNodeDao.getNodeListByTaskId(processTaskMain.getTaskId());
		Integer turnedDownNodeSeq = -1;
		ProcessTaskNode endNode = null;
		List<String> deleteNodeList = new ArrayList<String>();
		for (ProcessTaskNode node : nodeList) {
			if (node.getNodeStatus() == ProcessTaskNode.NODE_STATUS_TURNED_DOWN) {
				turnedDownNodeSeq = node.getNodeSeq();
			} else if (node.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {
				endNode = node;
			} else if (node.getNodeStatus() == ProcessTaskNode.NODE_STATUS_PENDING_AUDIT) {
				deleteNodeList.add(node.getTaskNodeId());
			}
		}
		// 最后一个节点驳回
		if (!CollectionUtils.isEmpty(deleteNodeList)) {
			// 删除 多余的审核人
			int auditorEffectRow = taskNodeAuditorDao.deleteNodeAuditorByNodeIds(deleteNodeList);
			if (auditorEffectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],[{}]",
						processTaskMain.getReferId(), processTaskMain.getReferCode(), processTaskMain.getProcessName(),
						processTaskMain.getSystemModule(), "驳回新起流程,删除审核人失败");
				throw new RuntimeException("清理驳回流程失败");
			}
			// 删除节点
			int nodeEffectRow = processTaskNodeDao.deleteNodeByIds(deleteNodeList);
			if (nodeEffectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],[{}]",
						processTaskMain.getReferId(), processTaskMain.getReferCode(), processTaskMain.getProcessName(),
						processTaskMain.getSystemModule(), "驳回新起流程,删除节点失败");
				throw new RuntimeException("清理驳回流程失败");
			}
			// 修改结束节点的nodeSeq
			endNode.setNodeSeq(turnedDownNodeSeq + 1);
			int effectRow = processTaskNodeDao.updateById(endNode);
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],[{}]",
						processTaskMain.getReferId(), processTaskMain.getReferCode(), processTaskMain.getProcessName(),
						processTaskMain.getSystemModule(), "驳回新起流程,修改结束节点的nodeSeq失败");
				throw new RuntimeException("清理驳回流程失败");
			}
		}

		// 修改流程状态为 处理中
		processTaskMain.setProcessStatus(ProcessTaskMain.PROECESS_STATUS_HANDING);
		int taskEffectRow = processTaskMainDao.updateById(processTaskMain);
		if (taskEffectRow <= 0) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],systemModule:[{}],[{}]",
					processTaskMain.getReferId(), processTaskMain.getReferCode(), processTaskMain.getProcessName(),
					processTaskMain.getSystemModule(), "驳回新起流程,修改流程状态失败");
			throw new RuntimeException("修改流程状态失败");
		}
		return endNode;
	}

	/**
	 * build nodeAuditor
	 * 
	 * @param nodeTemplate
	 * @param auditor
	 * @return
	 */
	private TaskNodeAuditor initNodeAuditor(ProcessTaskNode taskNode, User auditor, int nodeSeq) {
		// 节点审核人
		TaskNodeAuditor nodeAuditor = new TaskNodeAuditor();
		nodeAuditor.setTaskNodeId(taskNode.getTaskNodeId());// 流程实例节点ID
		if (taskNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)) {
			nodeAuditor.setAuditorStartDate(new Date()); // 审核开始时间
			nodeAuditor.setAuditorDate(new Date());
			nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDIT_PASS);// 通过
		} else {
			if (nodeSeq == 1) {
				nodeAuditor.setAuditorStartDate(new Date()); // 审核开始时间
				nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDITING);// 审查状态
			} else {
				nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_PENDING_AUDIT);// 审查状态
			}
		}

		nodeAuditor.setAuditorNo(auditor.getNo());// 审核人
		String auditorDes = auditor.getCname() + "-" + auditor.getCode();
		if (StringUtils.isNotBlank(auditor.getPhone())) {
			auditorDes = auditorDes + " (" + auditor.getPhone() + ")";
		}
		if (StringUtils.isNotBlank(auditor.getMobile())) {
			auditorDes = auditorDes + " (" + auditor.getMobile() + ")";
		}
		nodeAuditor.setAuditorName(auditor.getCname());// 审核人姓名
		nodeAuditor.setAuditorDes(auditorDes);// 审核人描述
		nodeAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_Y);// 审核权限:Y
		nodeAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_Y);// 显示到界面
		// 审核人类型（1，本人，2，加签，3，转移，4，知会）
		nodeAuditor.setAuditorType(TaskNodeAuditor.AUDITOR_TYPE_SELF);
		return nodeAuditor;
	}

	/**
	 * build taskNode
	 * 
	 * @param processTaskMain
	 * @param nodeTemplate
	 * @return
	 */
	private ProcessTaskNode buildTaskNode(ProcessTaskMain processTaskMain, ProcessNodeTemplate nodeTemplate,
			int nodeSeq, ProcessTaskNode parentTaskNode, int nodeSeqStart) {
		ProcessTaskNode taskNode = new ProcessTaskNode();
		taskNode.setTaskId(processTaskMain.getTaskId());// 主流程ID
		taskNode.setNodeTemplateId(nodeTemplate.getNodeId());// 流程节点Id
		taskNode.setNodeName(nodeTemplate.getNodeName());// 节点名称
		taskNode.setNodeCode(nodeTemplate.getNodeCode());// 节点编码
		taskNode.setNodeSeq(nodeSeqStart + nodeSeq);// 节点序列号
		taskNode.setTimeEffective(nodeTemplate.getTimeEffective());
		taskNode.setReferUrl(nodeTemplate.getReferUrl());// 动态表单URL
		taskNode.setSignType(nodeTemplate.getSignType());// 加签类型
		if (nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)) {// 提交节点
			taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_INIT);
			taskNode.setNodeType(ProcessNodeTemplate.NODE_TYPE_INIT);
		} else if (nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {// 结束节点
			taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_END);
			taskNode.setNodeType(ProcessNodeTemplate.NODE_TYPE_END);
		} else if (nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_DIRECT)) {// 直流节点
			taskNode.setNodeType(ProcessNodeTemplate.NODE_TYPE_DIRECT);
			taskNode.setRoleNo(nodeTemplate.getRoleNo());
			taskNode.setFormId(nodeTemplate.getFormId());
			taskNode.setIsSendEmail(nodeTemplate.getIsSendEmail());
			taskNode.setIsSendSms(nodeTemplate.getIsSendSms());
			taskNode.setIsSendWechat(nodeTemplate.getIsSendWechat());
			taskNode.setIsAutoAudit(nodeTemplate.getIsAutoAudit());
			if (nodeSeq == 1) { // 第二个为当前节点 ，审核中,其它 待审核
				Date nodeStartTime = new Date();
				taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_AUDITING);
				taskNode.setAuditorStartDate(nodeStartTime); // 审核开始时间
				// 节点超时时间计算
				int hours = nodeTemplate.getTimeEffective() == null ? 0 : nodeTemplate.getTimeEffective();
				Date nodeOvertime = calculateTimeUtils.calculateOvertime(nodeStartTime, hours);
				if (nodeOvertime != null) {
					taskNode.setNodeOvertime(nodeOvertime);
				}
			} else {// 待审核节点
				taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_PENDING_AUDIT);
			}
		}
		// 节点设置为链状结构
		if (parentTaskNode == null) {
			taskNode.setNodeParentId("-1");
		} else {
			taskNode.setNodeParentId(parentTaskNode.getTaskNodeId());
		}
		return taskNode;
	}

	/**
	 * @param requestParam
	 * @param processTemplate
	 * @param nodeTemplateList
	 * @return
	 */
	private ProcessTaskMain insertProcessTaskMain(WFRequestParam requestParam, ProcessTemplate processTemplate,
			List<ProcessNodeTemplate> nodeTemplateList) {
		// 实例化主流程
		ProcessTaskMain taskMain = new ProcessTaskMain();
		taskMain.setTaskCode(ProcessCodeBuilder.buildCode(processTemplate,requestParam.getReferCode(), requestParam.getProcessType()));
		taskMain.setProcessId(processTemplate.getProcessNo());
		taskMain.setProcessName(processTemplate.getProcessName());
		taskMain.setProcessType(processTemplate.getProcessType());
		taskMain.setSystemModule(processTemplate.getSystemModule());
		taskMain.setProcessVersion(processTemplate.getProcessVersion());
		taskMain.setReferId(requestParam.getReferId());
		taskMain.setReferCode(requestParam.getReferCode());
		taskMain.setReferUrl(requestParam.getReferUrl());
		taskMain.setAppliedby(requestParam.getAppliedby());
		taskMain.setAppliedbyName(requestParam.getAppliedbyName());
		taskMain.setAppliedDate(new Date()); // 流程申请时间
		taskMain.setIsSystemStart(ProcessTaskMain.SYSTEM_START_NO);
		taskMain.setAuditorStime(new Date());// 审批开始时间
		taskMain.setProcessStatus(ProcessTaskMain.PROECESS_STATUS_HANDING);
		taskMain.setAuditorSteps(nodeTemplateList.size());// 审批总步骤
		taskMain.setAppliedDes(requestParam.getAppliedDes());
		taskMain.setAuditorCrrStep(2);// 当前步骤序号
		taskMain.setTimeEffective(processTemplate.getTimeEffective());
		ActOrgEntity orgEntity = sysOrgDao.getOrgUnitNo(requestParam.getOrgNo());
		if (null != orgEntity) {
			taskMain.setOrgId(requestParam.getOrgNo());
			taskMain.setOrgName(orgEntity.getOrgName());
		}
		ProcessTaskMain processTaskMain = processTaskMainDao.insert(taskMain);
		return processTaskMain;
	}

	/**
	 * 检查授权人是否在审核人列表里面
	 * 
	 * @param auditorList
	 * @param author
	 * @return
	 */
	private boolean checkExsitAuditor(List<TaskNodeAuditor> auditorList, User author) {
		for (TaskNodeAuditor auditor : auditorList) {
			if (StringUtils.equals(auditor.getAuditorNo(), author.getNo())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 处理流程授权
	 * 
	 * @param processId
	 */
	private void handProcessAuthorize(ProcessTaskMain taskMain) {
		List<ProcessTaskNode> taskNodeList = processTaskNodeDao.getTaskNode(taskMain.getTaskId(),
				ProcessTaskNode.NODE_STATUS_AUDITING);
		if (CollectionUtils.isEmpty(taskNodeList) || taskNodeList.size() > 1) {
			throw new RuntimeException("处理流程授权失败");
		}
		ProcessTaskNode taskNode = taskNodeList.get(0);
		List<TaskNodeAuditor> taskNodeAuditors = taskNodeAuditorDao
				.getNodeAuditorsWithPermission(taskNode.getTaskNodeId());
		if (CollectionUtils.isEmpty(taskNodeAuditors)) {
			throw new RuntimeException("处理流程授权失败");
		}

		for (TaskNodeAuditor auditor : taskNodeAuditors) {
			User author = processAuthorizationDao.findProcessAuthPerson(auditor.getAuditorNo(),
					taskMain.getProcessType());
			// 有流程转交
			if (author != null && !checkExsitAuditor(taskNodeAuditors, author)) {
				// 1.修改自己的状态，并且不展示到界面
				auditor.setOperId("10000");
				auditor.setOperName("system");
				auditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_N);// 不显示到界面
				auditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_N);// 无审核权限
				// 转移，自己审核状态为通过
				auditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDIT_PASS);
				auditor.setAuditorDate(new Date());
				auditor.setAuditNotes("授权转交给(" + author.getCname() + "-" + author.getCode() + ")");
				int effectRow = taskNodeAuditorDao.updateById(auditor);
				if (effectRow <= 0) {
					log.info("processId:[{}],nodeId:[{}],auditorName:[{}],授权转交,修改节点审核人信息失败", taskMain.getTaskId(),
							taskNode.getTaskNodeId(), auditor.getAuditorName());
					throw new RuntimeException("授权转交失败");
				}

				// 新加审核人
				TaskNodeAuditor authorAuditor = new TaskNodeAuditor();
				authorAuditor.setTaskNodeId(taskNode.getTaskNodeId());
				authorAuditor.setAuditorStartDate(new Date());// 审核开始时间
				authorAuditor.setAuditorNo(author.getNo());
				authorAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_Y);// 有审核权限
				authorAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_Y);// 展示到界面
				authorAuditor.setAdderId(auditor.getAuditorNo());
				authorAuditor.setAdderName(auditor.getAuditorName());
				// 转交描述
				String auditorDes = author.getCname() + "-" + author.getCode();
				if (StringUtils.isNotBlank(author.getPhone())) {
					auditorDes = auditorDes + "(" + author.getPhone() + ")";
				}
				if (StringUtils.isNotBlank(author.getMobile())) {
					auditorDes = auditorDes + "(" + author.getMobile() + ")";
				}
				authorAuditor.setAuditorName(author.getCname());
				authorAuditor.setAuditorDes(auditorDes);
				// 转交意见
				String auditNotes = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + auditor.getAuditorName()
						+ "授权转交给" + author.getCname();
				authorAuditor.setAuditNotes(auditNotes);
				authorAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDITING);// 审核中
				authorAuditor.setAuditorType(TaskNodeAuditor.AUDITOR_TYPE_MOVE); // 转移
				TaskNodeAuditor saveAuditor = taskNodeAuditorDao.insert(authorAuditor);
				if (saveAuditor == null) {
					log.info("processId:[{}],nodeId:[{}],auditorName:[{}],授权转交,新增授权人信息失败", taskMain.getTaskId(),
							taskNode.getTaskNodeId(), auditor.getAuditorName());
					throw new RuntimeException("授权转交失败");
				}

				// 发邮件,给待办人发邮件
				List<TaskNodeAuditor> toList = new ArrayList<TaskNodeAuditor>();
				toList.add(saveAuditor);
				wFEmailService.notifyEmailToDo(taskMain, toList, taskNode);
				wechatService.notifyWechatToDo(taskMain, toList, taskNode);
			}
		}
	}

	/**
	 * 查找所有节点的审核人
	 * 
	 * @return
	 */
	private ModelResult<Map<String, List<User>>> searchNodeAuditor(ProcessTemplate processTemplate, String orgNo,
			List<ProcessNodeTemplate> nodeTemplateList, Map<String, List<String>> roleUserMap, String appliedby) {
		ModelResult<Map<String, List<User>>> resultModel = new ModelResult<Map<String, List<User>>>();
		Map<String, List<User>> auditorMap = new HashMap<String, List<User>>();

		// 由发起的组织UnitNo，找出整个组织树
		List<Organ> organList = organService.getOrganTreeByUnitNo(orgNo);
		for (ProcessNodeTemplate nodeTemplate : nodeTemplateList) {
			if (!nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)
					&& !nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {
				// 每个节点审核人
				List<String> nodeAuditorList = new ArrayList<String>();
				boolean hasGivenRole = false;
				// 如果启动流程时指定了角色和经办人，则所有节点的这个角色的审核人都取传入的经办人
				if (roleUserMap != null && !roleUserMap.isEmpty()) {
					for (Map.Entry<String, List<String>> entry : roleUserMap.entrySet()) {
						String roleCode = entry.getKey();
						List<String> roleUserNoList = entry.getValue();
						if (roleUserNoList == null || roleUserNoList.isEmpty()) {
							log.info("roleCode:[{}],指定节点的经办角色不存在", roleCode);
							resultModel.withError("process.task.instance.param.roleUser.isNull",
									"指定节点角色为[" + roleCode + "]审核人不能为空");
							return resultModel;
						}
						SysRole sysRole = sysRoleDao.getByRoleCode(roleCode);
						if (null == sysRole) {
							log.info("roleCode:[{}],指定节点的经办角色不存在", roleCode);
							resultModel.withError("process.node.init.role.not.exist",
									"没有找到指定角色Code为[" + roleCode + "]的经办角色");
							return resultModel;
						}
						if (sysRole.getId().equals(nodeTemplate.getRoleNo())) {
							hasGivenRole = true;
							nodeAuditorList.addAll(roleUserNoList);
						}
					}
				}

				if (!hasGivenRole) {
					// 判断角色是否递归查找
					SysRole sysRole = sysRoleDao.getById(nodeTemplate.getRoleNo());
					if (sysRole == null) {
						log.info("processName:[{}],systemModule:[{}],nodeName:[{}],查找角色失败",
								processTemplate.getProcessName(), processTemplate.getSystemModule(),
								nodeTemplate.getNodeName());
						resultModel.withError("node.no.auditor", "[" + processTemplate.getProcessName() + "-->"
								+ nodeTemplate.getNodeName() + "],查找角色失败");
						return resultModel;
					}

					// 判断是否为虚拟角色
					if (checkVirRole(sysRole)) {
						String hrAuditorNo = getVirRoleAuditor(appliedby, sysRole);
						if (StringUtils.isNotBlank(hrAuditorNo)) {
							nodeAuditorList.add(hrAuditorNo);
						}
					} else {
						List<OrgRoleUser> auditorOrganList = new ArrayList<OrgRoleUser>();
						// 从下往上找，有这个权限的组织
						for (Organ org : organList) {
							// 查找该组织是否具有该权限
							List<OrgRoleUser> orgRoleUserList = organRoleUserDao.getOrganRoleUser(org.getOrgId(),
									nodeTemplate.getRoleNo());
							auditorOrganList.addAll(orgRoleUserList);
							// 平台角色不递归
							if (sysRole.getRoleLevel().equals("9")) {
								break;
							}
						}
						for (OrgRoleUser oru : auditorOrganList) {
							nodeAuditorList.add(oru.getUserNo());
						}
					}

					// 该节点没有配置审核人
					if (CollectionUtils.isEmpty(nodeAuditorList)) {
						log.info("processName:[{}],systemModule:[{}],nodeName:[{}],没有查找到相关审核人或员工已离职",
								processTemplate.getProcessName(), processTemplate.getSystemModule(),
								nodeTemplate.getNodeName());
						resultModel.withError("node.no.auditor", "[" + processTemplate.getProcessName() + "-->"
								+ nodeTemplate.getNodeName() + "],没有查找到相关审核人或员工已离职");
						return resultModel;
					}
				}

				// 根据组织和角色找人
				List<User> userList = sysUserDao.getUserByNos(nodeAuditorList);
				if (CollectionUtils.isEmpty(userList)) {
					log.info("processName:[{}],systemModule:[{}],nodeName:[{}],没有查找到相关审核人或员工已离职",
							processTemplate.getProcessName(), processTemplate.getSystemModule(),
							nodeTemplate.getNodeName());
					resultModel.withError("node.no.auditor", "[" + processTemplate.getProcessName() + "-->"
							+ nodeTemplate.getNodeName() + "],没有查找到相关审核人或员工已离职");
					return resultModel;
				}
				// 保存节点 审核人
				auditorMap.put(nodeTemplate.getNodeId(), userList);
			}
		}
		return resultModel.withModel(auditorMap);
	}

	/**
	 * 检查是否是虚拟角色
	 * 
	 * @param sysRole
	 * @return
	 */
	private boolean checkVirRole(SysRole sysRole) {
		if (StringUtils.equals("ZGSPR", sysRole.getRoleCode()) || StringUtils.equals("BMSPR", sysRole.getRoleCode())
				|| StringUtils.equals("FYSPR", sysRole.getRoleCode())
				|| StringUtils.equals("JZSPR", sysRole.getRoleCode())
				|| StringUtils.equals("PAY-PTXDJL", sysRole.getRoleCode())) {
			return true;
		}
		return false;
	}

	/**
	 * 获取虚拟角色审核人
	 * 
	 * @param employeeNo
	 * @param sysRole
	 * @return
	 */
	private String getVirRoleAuditor(String appliedby, SysRole sysRole) {
		if (StringUtils.equals("PAY-PTXDJL", sysRole.getRoleCode())) {
			return appliedby;
		}
		String auditorType = null;
		// 主管审批人
		if (StringUtils.equals("ZGSPR", sysRole.getRoleCode())) {
			auditorType = HREmployeeAuditor.AUDITOR_TYPE_DIR;
		} else if (StringUtils.equals("BMSPR", sysRole.getRoleCode())) {
			auditorType = HREmployeeAuditor.AUDITOR_TYPE_DEP;
		} else if (StringUtils.equals("FYSPR", sysRole.getRoleCode())) {
			auditorType = HREmployeeAuditor.AUDITOR_TYPE_FEE;
		} else if (StringUtils.equals("JZSPR", sysRole.getRoleCode())) {
			auditorType = HREmployeeAuditor.AUDITOR_TYPE_ASS;
		}
		if (StringUtils.isBlank(auditorType)) {
			return null;
		}
		return sysUserDao.getEmployeeAuditor(appliedby, auditorType).getAuditorNo();
	}

	@Transactional("wfTransactionManager")
	@Override
	public ModelResult<NodeTransferResult> transferNode(TransferNodeParam transferNodeParam) {
		log.info("[{}],开始节点流转", transferNodeParam);
		ModelResult<NodeTransferResult> transferResult = new ModelResult<NodeTransferResult>();
		ModelResult<Boolean> checkModelResult = WFParamCheckUtils.checkNodeTransferParam(transferNodeParam);
		if (!checkModelResult.isSuccess()) {
			log.info(checkModelResult.getErrMsg());
			return transferResult.withError(checkModelResult.getErrCode(), checkModelResult.getErrMsg());
		}

		// 查询意见审核人
		TaskNodeAuditor taskNodeAuditor = taskNodeAuditorDao.getById(TaskNodeAuditor.class,
				transferNodeParam.getNodeAuditorId());
		if (taskNodeAuditor == null) {
			log.info("NodeAuditorId:[{}],节点流转,节点审核人不存在", transferNodeParam.getNodeAuditorId());
			return transferResult.withError("process.node.transter.NodeAuditor.not.exist", "节点审核人不存在");
		}
		// 审核人状态异常
		if (taskNodeAuditor.getAuditorStatus() != TaskNodeAuditor.AUDITOR_STATUS_AUDITING) {
			log.info("NodeAuditorId:[{}],NodeAuditorStatus:[{}],节点流转,该审核人状态异常", transferNodeParam.getNodeAuditorId(),
					taskNodeAuditor.getAuditorStatus());
			return transferResult.withError("process.node.transter.NodeAuditor.stauts.error", "该审核人状态异常");
		}

		// 检查是否是超级管理员
		User loginUser = UserHolder.getUser();
		// 是否为超级管理员
		boolean isAdmin = loginUser.isSuperAdministrator();
		if (!isAdmin) {
			if (!loginUser.getNo().equals(taskNodeAuditor.getAuditorNo())) {
				log.info("NodeAuditorId:[{}],auditorNo:[{}],loginUserNo:[{}],节点流转,流程节点审核人无效",
						transferNodeParam.getNodeAuditorId(), taskNodeAuditor.getAuditorNo(), loginUser.getNo());
				return transferResult.withError("process.node.transter.auditors.invalid", "流程节点审核人无效");
			}
		}

		// 异常校验
		int actionId = transferNodeParam.getActionId();
		if (actionId == TransferNodeParam.ACTION_SIGN) {
			// 加签
			if (loginUser.getNo().equals(transferNodeParam.getAdderId())) {
				log.info("NodeAuditorId:[{}],auditorNo:[{}],loginUserNo:[{}],节点流转,不能加签给自己",
						transferNodeParam.getNodeAuditorId(), taskNodeAuditor.getAuditorNo(), loginUser.getNo());
				return transferResult.withError("process.node.transter.sign.to.self", "不能加签给自己");
			}
		} else if (actionId == TransferNodeParam.ACTION_CHANGE) {
			// 转交
			if (loginUser.getNo().equals(transferNodeParam.getAdderId())) {
				log.info("NodeAuditorId:[{}],auditorNo:[{}],loginUserNo:[{}],节点流转,不能转交给自己",
						transferNodeParam.getNodeAuditorId(), taskNodeAuditor.getAuditorNo(), loginUser.getNo());
				return transferResult.withError("process.node.transter.move.to.self", "不能转交给自己");
			}
		}

		// 查询流程节点
		ProcessTaskNode taskNode = processTaskNodeDao.getById(ProcessTaskNode.class, taskNodeAuditor.getTaskNodeId());
		if (taskNode == null) {
			log.info("NodeAuditorId:[{}],流程可审核的节点不存在", transferNodeParam.getNodeAuditorId());
			return transferResult.withError("process.node.transter.audit.node.not.exist", "流程可审核的节点不存在");
		}
		// 流程节点状态异常
		if (taskNode.getNodeStatus() != ProcessTaskNode.NODE_STATUS_AUDITING) {
			log.info("NodeAuditorId:[{}],nodeName:[{}],nodeStatus:[{}],流程节点状态异常", transferNodeParam.getNodeAuditorId(),
					taskNode.getNodeName(), taskNode.getNodeStatus());
			return transferResult.withError("process.node.transter.audit.node.status.error", "流程节点状态异常");
		}
		// 加签类型校验
		if (actionId == TransferNodeParam.ACTION_SIGN && StringUtils.isNotBlank(taskNode.getSignType())
				&& transferNodeParam.getSignType() == null) {
			return transferResult.withError("process.node.transter.sign.type.error", "加签类型不能为空");
		}

		// 查询主流程
		ProcessTaskMain taskMain = processTaskMainDao.getById(ProcessTaskMain.class, taskNode.getTaskId());
		if (taskMain == null) {
			log.info("NodeAuditorId:[{}],节点流转,流程不存在", transferNodeParam.getNodeAuditorId());
			return transferResult.withError("process.node.transter.task.not.exist", "流程不存在");
		}
		// 检查流程状态是否异常
		if (taskMain.getProcessStatus() != ProcessTaskMain.PROECESS_STATUS_HANDING) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],processStatus:[{}],节点流转，主流程状态异常",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
					taskMain.getProcessStatus());
			return transferResult.withError("process.node.transter.taskInstance.status.error", "主流程状态异常");
		}

		// 审核动作ID, 加签动作的审核通过不需要check
		if (actionId == TransferNodeParam.ACTION_PASS
				&& TaskNodeAuditor.AUDITOR_TYPE_SIGN != taskNodeAuditor.getAuditorType()) {
			// 调用业务 Check 接口
			WFNodeCheckParam checkParam = new WFNodeCheckParam();
			checkParam.setReferId(taskMain.getReferId());
			checkParam.setReferCode(taskMain.getReferCode());
			checkParam.setSystemModule(taskMain.getSystemModule());
			checkParam.setProcessType(taskMain.getProcessType());
			checkParam.setProcessVersion(taskMain.getProcessVersion());
			checkParam.setRoleNo(taskNode.getRoleNo());
			checkParam.setReferUrl(taskNode.getReferUrl());
			checkParam.setAuditorId(taskNodeAuditor.getAuditorNo());
			checkParam.setAuditorName(taskNodeAuditor.getAuditorName());
			checkParam.setAction(transferNodeParam.getActionId());
			checkParam.setNodeCode(taskNode.getNodeCode());
			log.info("referId:[{}],referUrl:[{}}开始调用业务Check接口", taskMain.getReferId(), taskMain.getReferUrl());
			log.info("teask node object:[{}}", taskNode.toString());
			ModelResult<Boolean> modelResult = workFlowNodeTransferCheck.check(checkParam);
			if (!modelResult.isSuccess() || !modelResult.getModel()) {
				log.info(
						"referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],errMsg:[{}],表单数据校验失败",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						taskNode.getNodeName(), taskNodeAuditor.getAuditorName(), modelResult.getErrMsg());
				return transferResult.withError(modelResult.getErrCode(), modelResult.getErrMsg());
			}
		}

		// 操作节点流转 1.先修改审核人的状态 ，2.再修改节点状态，3.最后修改流程状态
		if (actionId == TransferNodeParam.ACTION_PASS) {
			// 审核通过
			processNodeAuditorsByPass(transferNodeParam, taskNodeAuditor, taskMain, taskNode, loginUser);
		} else if (actionId == TransferNodeParam.ACTION_SIGN) {
			// 加签
			processNodeAuditorsBySign(transferNodeParam, taskNode, taskNodeAuditor, taskMain, loginUser);
		} else if (actionId == TransferNodeParam.ACTION_CHANGE) {
			// 转交
			processNodeAuditorsByChange(transferNodeParam, taskNode, taskNodeAuditor, taskMain, loginUser);
		} else if (actionId == TransferNodeParam.ACTION_TURNED_DOWN) {
			// 驳回
			processNodeAuditorsByTurnedDown(transferNodeParam, taskNodeAuditor, taskMain, taskNode, loginUser);
		} else if (actionId == TransferNodeParam.ACTION_TAKE_BACK) {
			// 收回
		} else {
			throw new RuntimeException("非法操作");
		}

		ProcessTaskNode nextTaskNode = null;
		// 计算节点状态,流程状态
		int taskNodeStatus = calcTaskNodeStatus(taskNode);
		if (taskNodeStatus == ProcessTaskNode.NODE_STATUS_AUDIT_PASS) { // 节点通过
			taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_AUDIT_PASS);
			// 修改节点状态
			int effectRow = processTaskNodeDao.updateNodeStatus(taskNode.getTaskNodeId(),
					ProcessTaskNode.NODE_STATUS_AUDIT_PASS, new Date());
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],审核失败,更新流程节点状态失败",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						taskNode.getNodeName(), taskNodeAuditor.getAuditorName());
				throw new RuntimeException("审核失败,更新流程节点状态失败");
			}
			// 启动下一个节点
			nextTaskNode = startNextNodeAfterPass(taskNode, taskMain);
			if(!nextTaskNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)){
				// 处理流程授权
				handProcessAuthorize(taskMain);
			}
		} else if (taskNodeStatus == ProcessTaskNode.NODE_STATUS_TURNED_DOWN) {// 节点驳回
			taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_TURNED_DOWN);
			// 修改节点状态
			int effectRow = processTaskNodeDao.updateNodeStatus(taskNode.getTaskNodeId(),
					ProcessTaskNode.NODE_STATUS_TURNED_DOWN, new Date());
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],审核失败,更新流程节点状态失败",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						taskNode.getNodeName(), taskNodeAuditor.getAuditorName());
				throw new RuntimeException("审核失败,更新流程节点状态失败");
			}

			// 驳回，流程结束
			taskMain.setProcessStatus(ProcessTaskMain.PROECESS_STATUS_TURNED_DOWN); // 流程状态驳回
			taskMain.setAuditorEtime(new Date()); // 设置结束时间
			effectRow = processTaskMainDao.updateById(taskMain);
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],驳回失败,更新主流程状态失败",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						taskNode.getNodeName(), taskNodeAuditor.getAuditorName());
				throw new RuntimeException("驳回失败,更新主流程状态失败");
			}
			// 通知业务系统状态变更
			notifyBizSystem(taskMain, taskNode);
			// 发邮件,给经办人发邮件
			List<String> toList = new ArrayList<String>();
			toList.add(taskMain.getAppliedby());
			wFEmailService.notifyEmailToOperator(taskMain, toList);
			// 发短信
			log.info("referCode:[{}],taskCode:[{}],messageObj:[{}],准备发送短信", taskMain.getReferCode(),
					taskMain.getTaskCode());
			wfSmsService.notifySMSToOperator(taskMain, toList);
		}
		log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],节点流转成功",
				taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
				taskNodeAuditor.getAuditorName());

		NodeTransferResult nodeTransferResult = new NodeTransferResult();
		nodeTransferResult.setNextTaskNode(nextTaskNode);// Next Node
		nodeTransferResult.setAuditorNo(taskNodeAuditor.getAuditorNo()); // AuditorNo
		return new ModelResult<NodeTransferResult>().withModel(nodeTransferResult);
	}

	/**
	 * 驳回
	 * 
	 * @param transferNodeParam
	 * @param nodeAuditor
	 */
	private void processNodeAuditorsByTurnedDown(TransferNodeParam transferNodeParam, TaskNodeAuditor nodeAuditor,
			ProcessTaskMain taskMain, ProcessTaskNode taskNode, User loginUser) {
		nodeAuditor.setOperId(loginUser.getNo()); // 实际操作人
		nodeAuditor.setOperName(loginUser.getCname());
		nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_TURNED_DOWN);
		nodeAuditor.setAuditorDate(new Date());// 审核时间
		String auditNote = transferNodeParam.getAuditNote();
		// 转交 追加意见
		if (nodeAuditor.getAuditorType() == TaskNodeAuditor.AUDITOR_TYPE_MOVE) {
			auditNote = nodeAuditor.getAuditNotes() + " " + auditNote;
		}
		nodeAuditor.setAuditNotes(auditNote);// 审核意见
		// 修改审核人的状态
		int effectRow = taskNodeAuditorDao.updateById(nodeAuditor);
		if (effectRow <= 0) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],驳回,修改节点审核人信息失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("审核失败"); // 回滚事物
		}
	}

	/**
	 * 通知业务系统
	 * 
	 * @return
	 */
	private void notifyBizSystem(ProcessTaskMain taskMain, ProcessTaskNode taskNode) {
		// 流程结束
		WFNotifyParam notifyParam = new WFNotifyParam();
		notifyParam.setReferId(taskMain.getReferId());
		notifyParam.setProcessType(taskMain.getProcessType());
		notifyParam.setProcessId(taskMain.getTaskId());
		notifyParam.setProcessName(taskMain.getProcessName());
		notifyParam.setSystemModule(taskMain.getSystemModule());
		notifyParam.setAppliedby(taskMain.getAppliedby());
		notifyParam.setAppliedbyName(taskMain.getAppliedbyName());
		notifyParam.setLastUpdateTime(taskMain.getAppliedDate());
		notifyParam.setProcessStatus(taskMain.getProcessStatus());
		notifyParam.setTaskNode(taskNode);
		log.info(notifyParam + ",开始通知业务系统");

		// mq消息通知
		Map<String, Boolean> notifyMsg = new HashMap<>();
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_PLACE_ON_FILE, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_ARCHIVE, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_CREDIT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_CREDI_FQ, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_CREDI_BP, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_SPEC_BANK_REPORT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_INAUDIT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_INAUDIT_FQ, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_INAUDIT_BP, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_BANK_REPORT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_BANK_REPORT_FQ, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_DEPOSIT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_DEPOSIT_FQ, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_REFUND_MGR_FEE, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_REFUND_MGR_FEE_FQ, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROECESS_TYPE_FINANCE_AUDIT, Boolean.TRUE);
		notifyMsg.put(ProcessTemplate.PROCESS_ACCESS, Boolean.TRUE);
		//notifyMsg.put(ProcessTemplate.CASE_SUBMIT, Boolean.TRUE);
		Boolean notifyMsgBool = notifyMsg.get(notifyParam.getProcessType());
		if (notifyMsgBool != null && notifyMsgBool) {
			try {
				Message message = new Message(CommonConst.notify_msg_type, JSON.toJSONString(notifyParam));
				producer.sendMessage(message);
				log.info("RabbitMq消息发送成功[工作流通知回调接口]");
			} catch (Exception e) {
				log.info("RabbitMq消息发送失败[工作流通知回调接口]," + e);
				throw new RuntimeException("RabbitMq消息发送失败," + e);
			}
		} else {
			try {
				ModelResult<Boolean> notifyResult = workFlowNotify.wfNotify(notifyParam);
				if (!notifyResult.isSuccess()) {
					log.info(notifyParam + ",开始通知业务系统失败," + notifyResult.getErrMsg());
				} else {
					log.info(notifyParam + ",通知业务系统成功");
				}
			} catch (Exception e) {
				log.info(taskMain.getProcessName() + ",通知业务系统失败...", e);
			}
		}

	}

	/**
	 * 通过
	 * 
	 * @param transferNodeParam
	 * @param nodeAuditor
	 * @throws RuntimeException
	 */
	private void processNodeAuditorsByPass(TransferNodeParam transferNodeParam, TaskNodeAuditor nodeAuditor,
			ProcessTaskMain taskMain, ProcessTaskNode taskNode, User loginUser) {
		nodeAuditor.setOperId(loginUser.getNo()); // 实际操作人
		nodeAuditor.setOperName(loginUser.getCname());
		nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDIT_PASS);
		nodeAuditor.setAuditorDate(new Date());// 审核时间
		String auditNote = transferNodeParam.getAuditNote();
		// 加签,转交 追加意见
		if (nodeAuditor.getAuditorType() == TaskNodeAuditor.AUDITOR_TYPE_SIGN) {
			auditNote = nodeAuditor.getAuditNotes() + "[加签反馈意见]: " + auditNote + "<br/>";
		}
		nodeAuditor.setAuditNotes(auditNote);// 审核意见
		// 修改审核人的状态
		int effectRow = taskNodeAuditorDao.updateById(nodeAuditor);
		if (effectRow <= 0) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],通过,修改节点审核人信息失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					loginUser.getCname());
			throw new RuntimeException("审核失败"); // 回滚事物
		}

		// 如果是被加签人,需要给加签人发邮件
		if (nodeAuditor.getAuditorType() == TaskNodeAuditor.AUDITOR_TYPE_SIGN) {
			// 加签回复描述
			String taskNodeRemark = StringUtils.trimToEmpty(taskNode.getRemark()) + "[" + nodeAuditor.getAuditorName()
					+ "]加签反馈[" + nodeAuditor.getAdderName() + "] 日期("
					+ DateFormatUtils.format(nodeAuditor.getAuditorDate(), "yyyy-MM-dd HH:mm:ss") + ")";
			taskNode.setRemark(taskNodeRemark);
			effectRow = processTaskNodeDao.updateById(taskNode);
			if (effectRow <= 0) {
				log.info(
						"referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],通过,修改taskNodeRemark信息失败",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						taskNode.getNodeName(), loginUser.getCname());
				throw new RuntimeException("审核失败"); // 回滚事物
			}
			// 找出，自动主动加签人，发邮件
			List<TaskNodeAuditor> signList = taskNodeAuditorDao.getNodeAuditorsWithAdderId(taskNode.getTaskNodeId(),
					nodeAuditor.getAdderId());
			wFEmailService.notifyEmailToMainSign(taskMain, signList, taskNode);
			// 发短信
			wfSmsService.notifySMSToMainSign(taskMain, signList, WFSmsServiceImpl.SMS_TO_SIGN_PASS, loginUser,
					nodeAuditor, taskNode);
		}
	}

	/**
	 * 转移
	 * 
	 * @param transferNodeParam
	 * @param taskNode
	 * @param nodeAuditor
	 */
	private void processNodeAuditorsByChange(TransferNodeParam transferNodeParam, ProcessTaskNode taskNode,
			TaskNodeAuditor nodeAuditor, ProcessTaskMain taskMain, User loginUser) {
		// 1.修改自己的状态，并且不展示到界面
		nodeAuditor.setOperId(loginUser.getNo());
		nodeAuditor.setOperName(loginUser.getCname());
		nodeAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_N);// 不显示到界面
		nodeAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_N);// 无审核权限
		// 转移，自己审核状态为通过
		nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDIT_PASS);
		nodeAuditor.setAuditorDate(new Date());
		nodeAuditor.setAuditNotes(transferNodeParam.getAuditNote());
		int effectRow = taskNodeAuditorDao.updateById(nodeAuditor);
		if (effectRow <= 0) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],转移,修改节点审核人信息失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("转交失败");
		}

		// 新加审核人
		TaskNodeAuditor signAuditor = new TaskNodeAuditor();
		signAuditor.setTaskNodeId(taskNode.getTaskNodeId());
		signAuditor.setAuditorStartDate(new Date());// 审核开始时间
		signAuditor.setAuditorNo(transferNodeParam.getAdderId());
		signAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_Y);// 有审核权限
		signAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_Y);// 展示到界面
		signAuditor.setAdderId(nodeAuditor.getAuditorNo());
		signAuditor.setAdderName(nodeAuditor.getAuditorName());
		User changer = sysUserDao.getSysUserById(transferNodeParam.getAdderId());
		if (changer == null) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],changeName:[{}],找不到转交人",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					transferNodeParam.getAdderName());
			throw new RuntimeException("找不到转交人"); // 回滚事物
		}
		// 转交描述
		String auditorDes = changer.getCname() + "-" + changer.getCode();
		if (StringUtils.isNotBlank(changer.getPhone())) {
			auditorDes = auditorDes + " (" + changer.getPhone() + ")";
		}
		if (StringUtils.isNotBlank(changer.getMobile())) {
			auditorDes = auditorDes + " (" + changer.getMobile() + ")";
		}
		signAuditor.setAuditorName(changer.getCname());
		signAuditor.setAuditorDes(auditorDes);
		// 转交意见
		String auditNotes = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + loginUser.getCname() + "转交给"
				+ changer.getCname();
		signAuditor.setAuditNotes(auditNotes);
		signAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDITING);// 审核中
		signAuditor.setAuditorType(TaskNodeAuditor.AUDITOR_TYPE_MOVE); // 转移
		TaskNodeAuditor saveAuditor = taskNodeAuditorDao.insert(signAuditor);
		if (saveAuditor == null) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],转移,添加转移人失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("转交失败");
		}

		// 转交描述
		String taskNodeRemark = StringUtils.trimToEmpty(taskNode.getRemark()) + "[" + nodeAuditor.getAuditorName()
				+ "]转交给[" + changer.getCname() + "] 日期("
				+ DateFormatUtils.format(saveAuditor.getAuditorStartDate(), "yyyy-MM-dd HH:mm:ss") + ")";
		taskNode.setRemark(taskNodeRemark);
		effectRow = processTaskNodeDao.updateById(taskNode);
		if (effectRow <= 0) {
			log.info(
					"referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],转移,修改taskNodeRemark信息失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("转交失败");
		}

		// 发邮件,给待办人发邮件
		List<TaskNodeAuditor> toList = new ArrayList<TaskNodeAuditor>();
		toList.add(saveAuditor);
		wFEmailService.notifyEmailToDo(taskMain, toList, taskNode);
		wechatService.notifyWechatToDo(taskMain, toList, taskNode);
	}

	/**
	 * 节点加签
	 * 
	 * @param transferNodeParam
	 * @param taskNode
	 * @param nodeAuditor
	 * @throws RuntimeException
	 */
	private void processNodeAuditorsBySign(TransferNodeParam transferNodeParam, ProcessTaskNode taskNode,
			TaskNodeAuditor nodeAuditor, ProcessTaskMain taskMain, User loginUser) {
		// 1.先修改节点的状态
		nodeAuditor.setOperId(loginUser.getNo());
		nodeAuditor.setOperName(loginUser.getCname());
		// 加签，自己审核状态不修改
		nodeAuditor.setAuditorDate(new Date());
		// nodeAuditor.setAuditNotes(transferNodeParam.getAuditNote());
		int effectRow = taskNodeAuditorDao.updateById(nodeAuditor);
		if (effectRow <= 0) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],加签,修改节点审核人信息失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("加签失败"); // 回滚事物
		}

		// 新增一个审核人,但是该人没审核权限
		TaskNodeAuditor signAuditor = new TaskNodeAuditor();
		signAuditor.setTaskNodeId(taskNode.getTaskNodeId());
		signAuditor.setAuditorStartDate(new Date()); // 审核开始时间
		signAuditor.setAuditorNo(transferNodeParam.getAdderId());
		signAuditor.setAdderId(nodeAuditor.getAuditorNo()); // 加签人ID
		signAuditor.setAdderName(nodeAuditor.getAuditorName()); // 加签人Name
		User signer = sysUserDao.getSysUserById(transferNodeParam.getAdderId());
		if (signer == null) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],signName:[{}],找不到加签人",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					transferNodeParam.getAdderName());
			throw new RuntimeException("找不到加签人"); // 回滚事物
		}
		// 加签描述
		String auditorDes = signer.getCname() + "-" + signer.getCode();
		if (StringUtils.isNotBlank(signer.getPhone())) {
			auditorDes = auditorDes + " (" + signer.getPhone() + " )";
		}
		if (StringUtils.isNotBlank(signer.getMobile())) {
			auditorDes = auditorDes + " (" + signer.getMobile() + " )";
		}
		signAuditor.setAuditorName(signer.getCname());
		signAuditor.setAuditorDes(auditorDes);
		// 加签意见
		String auditNotes = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " " + loginUser.getCname()
				+ " 加签给 " + signer.getCname() + "<br/>";
		// 加签类型
		if (transferNodeParam.getSignType() != null && transferNodeParam.getSignType().length > 0) {
			String signType = StringUtils.join(transferNodeParam.getSignType(), ",");
			signAuditor.setSignType(signType);
			List<String> signTypeList = Arrays.asList(transferNodeParam.getSignType());
			ModelResult<List<DicData>> modelResult = dicDataServiceInlet.getDicDataByTypeAndKeys("SIGN_TYPE",
					signTypeList);
			String signTypeDesc = "[加签类型]:";
			List<DicData> dicDataList = modelResult.getModel();
			for (DicData dic : dicDataList) {
				signTypeDesc = signTypeDesc + dic.getName() + ",";
			}
			auditNotes += signTypeDesc + "<br/>";
		}
		if (StringUtils.isNotBlank(transferNodeParam.getAuditNote())) {
			auditNotes += "[加签意见]:" + transferNodeParam.getAuditNote() + "<br/>";
		}
		signAuditor.setAuditNotes(auditNotes);
		signAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_AUDITING);// 审核中
		signAuditor.setAuditorType(TaskNodeAuditor.AUDITOR_TYPE_SIGN); // 审核人类型加签
		signAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_N);// 无审核权限
		signAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_Y);// 展示到界面
		TaskNodeAuditor saveAuditor = taskNodeAuditorDao.insert(signAuditor);
		if (saveAuditor == null) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],加签,添加加签人失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("加签失败");
		}

		// 节点加签描述
		String taskNodeRemark = StringUtils.trimToEmpty(taskNode.getRemark()) + "[" + nodeAuditor.getAuditorName()
				+ "]加签给[" + signer.getCname() + "] 日期("
				+ DateFormatUtils.format(saveAuditor.getAuditorStartDate(), "yyyy-MM-dd HH:mm:ss") + ")";
		taskNode.setRemark(taskNodeRemark);
		effectRow = processTaskNodeDao.updateById(taskNode);
		if (effectRow <= 0) {
			log.info(
					"referId:[{}],referCode:[{}],processName:[{}],nodeName:[{}],auditorName:[{}],加签,修改taskNode remark 失败",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName(),
					nodeAuditor.getAuditorName());
			throw new RuntimeException("加签失败");
		}

		// 发邮件,给待办人发邮件
		List<TaskNodeAuditor> toList = new ArrayList<TaskNodeAuditor>();
		toList.add(saveAuditor);
		wFEmailService.notifyEmailToDo(taskMain, toList, taskNode);
		// 发短信
		wfSmsService.notifySMSToMainSign(taskMain, toList, WFSmsServiceImpl.SMS_TO_SIGN, loginUser, nodeAuditor,
				taskNode);
		wechatService.notifyWechatToDo(taskMain, toList, taskNode);
	}

	/**
	 * 审核通过，启动下一个节点
	 * 
	 * @param taskNode
	 * @param taskMain
	 * @return 返回下一个节点
	 */
	private ProcessTaskNode startNextNodeAfterPass(ProcessTaskNode taskNode, ProcessTaskMain taskMain) {
		// 下一个启动的节点
		ProcessTaskNode nextNode = processTaskNodeDao.getNodeByParentId(taskNode.getTaskNodeId());
		if (nextNode == null) {
			log.info("referId:[{}],referCode:[{}],processName:[{}],启动下一个节点,查询下一个节点失败,currNodeName:[{}]",
					taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(), taskNode.getNodeName());
			throw new RuntimeException("启动下一个节点失败,节点不存在");
		}

		// 结束节点,结束流程
		if (nextNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {
			// 1.结束修改状态 --> 修改流程为结束
			taskMain.setProcessStatus(ProcessTaskMain.PROECESS_STATUS_PASS); // 流程状态结束
			taskMain.setAuditorEtime(new Date()); // 设置结束时间
			taskMain.setAuditorCrrStep(taskMain.getAuditorCrrStep() + 1);// 当前步数+1
			taskMain.setCurNodeId(nextNode.getTaskNodeId());// 更改流程当前节点
			taskMain.setCurNodeStime(new Date());

			int effectRow = processTaskMainDao.updateById(taskMain);
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],修改主流程状态为PASS,失败", taskMain.getReferId(),
						taskMain.getReferCode(), taskMain.getProcessName());
				throw new RuntimeException("审核失败");
			}
			// 通知业务系统状态变更
			notifyBizSystem(taskMain, taskNode);
			// 流程结束,给经办人发邮件
			List<String> toList = new ArrayList<String>();
			toList.add(taskMain.getAppliedby());
			wFEmailService.notifyEmailToOperator(taskMain, toList);
			// 发短信
			wfSmsService.notifySMSToOperator(taskMain, toList);
		} else {
			if (nextNode.getNodeStatus() != ProcessTaskNode.NODE_STATUS_PENDING_AUDIT) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],启动下一个节点,下一个节点状态错误,nextNodeName:[{}]",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						nextNode.getNodeName());
				throw new RuntimeException("启动下一个节点失败");
			}
			// 节点超时时间计算
			log.info("开始节点超时时间计算");
			Date nodeStartTime = new Date();
			int hours = nextNode.getTimeEffective() == null ? 0 : nextNode.getTimeEffective();
			Date nodeOvertime = calculateTimeUtils.calculateOvertime(nodeStartTime, hours);
			if (nodeOvertime != null) {
				nextNode.setNodeOvertime(nodeOvertime);
			}
			log.info("结束节点超时时间计算");
			// 修改节点状态
			nextNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_AUDITING); // 状态审核中
			nextNode.setAuditorStartDate(new Date()); // 审核开始时间
			int effectRow = processTaskNodeDao.updateById(nextNode);
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],启动下一个节点,修改节点信息失败,nextNodeName:[{}]",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						nextNode.getNodeName());
				throw new RuntimeException("审核失败");
			}

			// 修改主流程信息
			taskMain.setAuditorCrrStep(taskMain.getAuditorCrrStep() + 1);// 当前步数+1
			taskMain.setCurNodeId(nextNode.getTaskNodeId());// 更改流程当前节点
			taskMain.setCurNodeStime(new Date());
			effectRow = processTaskMainDao.updateById(taskMain);
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],启动下一个节点,修改主流程信息失败,nextNodeName:[{}]",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						nextNode.getNodeName());
				throw new RuntimeException("审核失败");
			}

			// 修改审核人的状态 和 审查开始时间
			effectRow = taskNodeAuditorDao.updateNodeAuditor(nextNode.getTaskNodeId(),
					TaskNodeAuditor.AUDITOR_STATUS_AUDITING, new Date());
			if (effectRow <= 0) {
				log.info("referId:[{}],referCode:[{}],processName:[{}],启动下一个节点,修改下个节点,审核人信息失败,nextNodeName:[{}]",
						taskMain.getReferId(), taskMain.getReferCode(), taskMain.getProcessName(),
						nextNode.getNodeName());
				throw new RuntimeException("审核失败");
			}

			// 通知业务系统状态变更
			notifyBizSystem(taskMain, taskNode);
			// 发邮件,给待办人发邮件
			List<TaskNodeAuditor> nextAuditorsList = taskNodeAuditorDao.getNodeAuditors(nextNode.getTaskNodeId());
			wFEmailService.notifyEmailToDo(taskMain, nextAuditorsList, nextNode);
			wechatService.notifyWechatToDo(taskMain, nextAuditorsList, nextNode);
		}
		return nextNode;
	}

	/**
	 * 计算流程节点状态
	 * 
	 * @param processTaskId
	 * @return
	 */
	private int calcTaskNodeStatus(ProcessTaskNode taskNode) {
		// 查询流程节点
		List<TaskNodeAuditor> auditorList = taskNodeAuditorDao.getNodeAuditorsWithPermission(taskNode.getTaskNodeId());
		// 默认审核中
		int taskNodeStatus = ProcessTaskNode.NODE_STATUS_AUDITING;
		// 通过人数
		int auditorPassCount = 0;
		// 驳回人数
		int auditorTurnedDown = 0;
		for (TaskNodeAuditor auditor : auditorList) {
			if (auditor.getAuditorStatus() == TaskNodeAuditor.AUDITOR_STATUS_AUDIT_PASS) {
				auditorPassCount++;
			} else if (auditor.getAuditorStatus() == TaskNodeAuditor.AUDITOR_STATUS_TURNED_DOWN) {
				auditorTurnedDown++;
			}
		}

		if (taskNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_DIRECT)) {// 单人审核
			if (auditorPassCount > 0) {
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_AUDIT_PASS;
			}
			if (auditorTurnedDown > 0) {
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_TURNED_DOWN;
			}
		} else if (taskNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_AND)) {// AND
			if (auditorPassCount == auditorList.size()) { // 全部通过，节点就通过
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_AUDIT_PASS;
			}
			if (auditorTurnedDown > 0) { // 一人驳回，节点就驳回
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_TURNED_DOWN;
			}
		} else if (taskNode.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_OR)) {// OR
			if (auditorPassCount > 0) { // 一人通过就通过
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_AUDIT_PASS;
			}
			if (auditorTurnedDown > 0) { // 一人驳回，节点就驳回
				taskNodeStatus = ProcessTaskNode.NODE_STATUS_TURNED_DOWN;
			}
		}
		return taskNodeStatus;
	}

	@Override
	@Transactional("wfTransactionManager")
	public ModelResult<Boolean> closeProcess(CloseProcessParam closeProcessParam) {
		log.info("开始关闭流程，[{}]", closeProcessParam);

		ModelResult<Boolean> modelResult = new ModelResult<>();
		// 校验参数
		if (!StringUtil.hasText(closeProcessParam.getReferId())) {
			return modelResult.withError("process.task.close.param.referid.isNull", "referid不能为空");
		}
		if (!StringUtil.hasText(closeProcessParam.getUserNo())) {
			return modelResult.withError("process.task.close.param.userNo.isNull", "userNo不能为空");
		}

		// 校验权限
		Boolean hasDelAuth = sysUserDao.checkUserRole(closeProcessParam.getUserNo(), SysRole.ROLE_DELPROCESS_CODE);
		if (!hasDelAuth) {
			log.error("用户没有关闭流程权限，param:{}", closeProcessParam);
			return modelResult.withError("process.task.close.noAuth", "用户没有关闭权限");
		}

		// 校验流程是否存在
		List<ProcessTaskMain> taskList = processTaskMainDao.getHandingProcess(closeProcessParam.getReferId());
		if (taskList != null && taskList.size() > 0) {
			// 关闭所有审批中的流程
			int effectRow = processTaskMainDao.closeProcessByReferId(closeProcessParam.getReferId());
			if (effectRow <= 0) {
				log.error("流程关闭失败，param:{}", closeProcessParam);
				return modelResult.withError("process.task.close.update.error", "流程关闭失败");
			}
		}

		log.info("流程关闭完成，[{}]", closeProcessParam);
		return modelResult.withModel(true);
	}

	@Override
	public List<Map<String, Object>> getProcessAuditors(String processType, String orgNo) {
		List<Map<String, Object>> auditorList = new ArrayList<Map<String, Object>>();
		List<ProcessTemplate> processTemplateList = new ArrayList<ProcessTemplate>();
		if (StringUtils.isNotBlank(processType)) {
			ProcessTemplate processTemplate = processTemplateDao.getPocessTemplate(ProcessTemplate.SYSTEM_MODULE_O2O,
					processType);
			processTemplateList.add(processTemplate);
		} else {
			List<ProcessTemplate> processTemplates = processTemplateDao
					.getPocessTemplateList(ProcessTemplate.SYSTEM_MODULE_O2O);
			processTemplateList.addAll(processTemplates);
		}
		if (processTemplateList.isEmpty()) {
			return auditorList;
		}

		for (ProcessTemplate processTemplate : processTemplateList) {
			// 检查流程节点模板是否存在
			List<ProcessNodeTemplate> nodeTemplateList = processNodeTemplateDao
					.getByProcessId(processTemplate.getProcessNo());
			if (nodeTemplateList == null || nodeTemplateList.isEmpty()) {
				continue;
			}

			// 由发起的组织No，找出整个组织树
			List<Organ> organList = organService.getOrganTreeByUnitNo(orgNo);
			if (organList == null || organList.isEmpty()) {
				continue;
			}

			Map<String, Object> processAuditorMap = new HashMap<String, Object>();
			processAuditorMap.put("processName", processTemplate.getProcessName());// 流程名称
			List<Object[]> auditors = new ArrayList<Object[]>();
			for (ProcessNodeTemplate nodeTemplate : nodeTemplateList) {
				if (nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)
						|| nodeTemplate.getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_END)) {
					Object[] auditor = new Object[] { nodeTemplate.getNodeName(), null };
					auditors.add(auditor);
				} else {
					// 判断角色是否递归查找
					SysRole sysRole = sysRoleDao.getById(nodeTemplate.getRoleNo());
					if (sysRole == null) {
						Object[] auditor = new Object[] { nodeTemplate.getNodeName(), null };
						auditors.add(auditor);
						continue;
					}
					// 虚拟角色
					if (checkVirRole(sysRole)) {
						Object[] auditor = new Object[] { nodeTemplate.getNodeName(), "虚拟角色" };
						auditors.add(auditor);
						continue;
					}
					List<OrgRoleUser> nodeAuditorList = new ArrayList<OrgRoleUser>();
					// 从下往上找，有这个权限的组织
					for (Organ org : organList) {
						// 查找该组织是否具有该权限
						List<OrgRoleUser> orgRoleUserList = organRoleUserDao.getOrganRoleUser(org.getOrgId(),
								nodeTemplate.getRoleNo());
						nodeAuditorList.addAll(orgRoleUserList);
						// 平台角色不递归
						if (sysRole.getRoleLevel().equals("9")) {
							break;
						}
					}

					if (CollectionUtils.isEmpty(nodeAuditorList)) {
						Object[] auditor = new Object[] { nodeTemplate.getNodeName(), null };
						auditors.add(auditor);
					} else {
						List<String> userNoList = new ArrayList<String>();
						for (OrgRoleUser oru : nodeAuditorList) {
							userNoList.add(oru.getUserNo());
						}
						// 根据组织和角色找人
						List<User> userList = sysUserDao.getUserByNos(userNoList);
						StringBuffer auditorBuffer = new StringBuffer();
						for (User user : userList) {
							String auditorDes = user.getCname() + "-" + user.getCode();
							if (StringUtils.isNotBlank(user.getPhone())) {
								auditorDes = auditorDes + "(" + user.getPhone() + ")";
							} else {
								auditorDes = auditorDes + "(" + user.getMobile() + ")";
							}
							auditorBuffer.append(auditorDes).append(",");
						}
						Object[] auditor = new Object[] { nodeTemplate.getNodeName(), auditorBuffer.toString() };
						auditors.add(auditor);
					}
				}
			}
			processAuditorMap.put("auditorList", auditors);
			auditorList.add(processAuditorMap);
		}
		return auditorList;
	}

	@Override
	@Transactional("wfTransactionManager")
	public ModelResult<Boolean> cancelProcess(CancleProcessParam param) {
		// 根据 referId + processType 查找流程
		List<ProcessTaskMain> taskList = processTaskMainDao.getTaskMainListByType(param.getReferId(),
				param.getProcessType());
		for (ProcessTaskMain task : taskList) {
			// 处理中的流程
			if (task.getProcessStatus() == ProcessTaskMain.PROECESS_STATUS_HANDING) {
				List<ProcessTaskNode> nodeList = processTaskNodeDao.getTaskNode(task.getTaskId(),
						ProcessTaskNode.NODE_STATUS_AUDITING);
				if (!CollectionUtils.isEmpty(nodeList) && nodeList.size() == 1) {
					ProcessTaskNode taskNode = nodeList.get(0);
					// 往改节点下添加一个审核人
					TaskNodeAuditor nodeAuditor = new TaskNodeAuditor();
					nodeAuditor.setTaskNodeId(taskNode.getTaskNodeId());// 流程实例节点ID
					nodeAuditor.setAuditorStartDate(new Date()); // 审核开始时间
					nodeAuditor.setAuditorDate(new Date());
					nodeAuditor.setAuditorStatus(TaskNodeAuditor.AUDITOR_STATUS_TURNED_DOWN);// 驳回
					nodeAuditor.setAuditorNo(param.getOperNo());// 审核人
					User auditor = null;
					if(StringUtils.equals(param.getOperNo(), "10000")){//系统管理员
						auditor = new User();
						auditor.setCode("10000");
						auditor.setCname("系统管理员");
					}else{
						auditor = sysUserDao.getSysUserById(param.getOperNo());
					}
					String auditorDes = auditor.getCname() + "-" + auditor.getCode();
					if (StringUtils.isNotBlank(auditor.getPhone())) {
						auditorDes = auditorDes + "(" + auditor.getPhone() + ")";
					}
					if (StringUtils.isNotBlank(auditor.getMobile())) {
						auditorDes = auditorDes + "(" + auditor.getMobile() + ")";
					}
					nodeAuditor.setAuditorName(auditor.getCname());// 审核人姓名
					nodeAuditor.setAuditorDes(auditorDes);// 审核人描述
					nodeAuditor.setAuditNotes("[作废原因]：" + param.getCancleReason());
					nodeAuditor.setHasAuditPermission(TaskNodeAuditor.AUDIT_PERMISSION_Y);// 审核权限:Y
					nodeAuditor.setIsDisplay(TaskNodeAuditor.IS_DISPLAY_Y);// 显示到界面
					nodeAuditor.setAuditorType(TaskNodeAuditor.AUDITOR_TYPE_SELF);
					TaskNodeAuditor saveAuditor = taskNodeAuditorDao.insert(nodeAuditor);
					if (saveAuditor == null) {
						throw new RuntimeException("写入作废人失败");
					}
					// 修改节点状态
					taskNode.setNodeStatus(ProcessTaskNode.NODE_STATUS_TURNED_DOWN);
					int effectRow = processTaskNodeDao.updateById(taskNode);
					if (effectRow <= 0) {
						throw new RuntimeException("修改节点状态失败");
					}
					// 关闭流程
					task.setProcessStatus(ProcessTaskMain.PROECESS_STATUS_CLOSE);
					effectRow = processTaskMainDao.updateById(task);
					if (effectRow <= 0) {
						throw new RuntimeException("修改流程状态失败");
					}
					
					if(!StringUtils.equals(task.getAppliedby(), "001") && !StringUtils.equals(task.getAppliedby(), "1")){
						List<String> toList = new ArrayList<String>();
						toList.add(task.getAppliedby());
						// 发短信
						log.info("referCode:[{}],taskCode:[{}],messageObj:[{}],准备发送短信", task.getReferCode(),
								task.getTaskCode());
						wfSmsService.notifySMSToOperator(task, toList);
					}
				}
			}
		}
		return new ModelResult<Boolean>().withModel(Boolean.TRUE);
	}

	@Override
	public ModelResult<Boolean> transferNodeWithAuto(TransferNodeParam transferNodeParam) {
		ModelResult<Boolean> resultModel = new ModelResult<Boolean>();
		ModelResult<NodeTransferResult> modelResult = transferNode(transferNodeParam);
		if (!modelResult.isSuccess()) {
			return resultModel.withError(modelResult.getErrCode(), modelResult.getErrMsg());
		}

		// 检查自动过单条件
		NodeTransferResult nodeTransferResult = modelResult.getModel();
		log.error("自动过单节点:[{}]", JSON.toJSONString(nodeTransferResult));
		while (nodeTransferResult != null && nodeTransferResult.getNextTaskNode() != null
				&& (!nodeTransferResult.getNextTaskNode().getNodeType().equals(ProcessNodeTemplate.NODE_TYPE_INIT)
						&& !nodeTransferResult.getNextTaskNode().getNodeType()
								.equals(ProcessNodeTemplate.NODE_TYPE_END))
				&& (nodeTransferResult.getNextTaskNode().getIsAutoAudit() != null
						&& nodeTransferResult.getNextTaskNode().getIsAutoAudit() == 1)
				&& StringUtils.isBlank(nodeTransferResult.getNextTaskNode().getReferUrl())) {
			try {
				List<TaskNodeAuditor> auditorList = taskNodeAuditorDao
						.getNodeAuditorsWithPermission(nodeTransferResult.getNextTaskNode().getTaskNodeId());
				if (CollectionUtils.isEmpty(auditorList)) {
					break;
				}
				// 审核人No
				String auditorNo = nodeTransferResult.getAuditorNo();
				boolean autoCondition = false;
				for (TaskNodeAuditor auditor : auditorList) {
					if (StringUtils.equals(auditorNo, auditor.getAuditorNo())) {
						autoCondition = true;
						transferNodeParam.setAuditNote("自动审核通过");
						transferNodeParam.setNodeAuditorId(auditor.getNodeAuditorId());
						break;
					}
				}

				if (!autoCondition) {
					break;
				}

				log.error("自动过单开始:[{}]", JSON.toJSONString(nodeTransferResult));
				modelResult = transferNode(transferNodeParam);
				if (!modelResult.isSuccess()) {
					return resultModel.withError(modelResult.getErrCode(), modelResult.getErrMsg());
				}

				nodeTransferResult = modelResult.getModel();
			} catch (Exception e) {
				log.error("自动过单失败", e);
				return resultModel.withError("自动过单失败", "自动过单失败");
			}
		}

		// 检查是否自动过单
		return resultModel.withModel(Boolean.TRUE);
	}
}